  
//
// 130601 - AH - Created.
// 130604 - AH - Bug fixes, got copy to work.
//

package org.soekrat.engine.socraton;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.japril.jutil.CharArrayPrintWriter;
import org.japril.jutil.IOUtil;
import org.xml.sax.SAXException;

/**
 * <p>
 *   Object that interact with the Socraton service.  
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC)
 */
public final class SocratonsService 
{
  private static SocratonsService c_instance=null;
  
  private static final String m_url="http://ahaxproject.appspot.com/socratons";
  
  private final String m_getReq;
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  private SocratonsService()
  {
    CharArrayPrintWriter capw=new CharArrayPrintWriter();
    capw.println("<?xml version='1.0'?>");
    capw.println("<socratons method='copy' />");
    m_getReq=capw.toString();
  }
  
  /**
   * <p>
   *   Gets instance to use.
   * </p>
   * @return Instance to use.
   */
  public synchronized static SocratonsService getInstance()
  {
    if (c_instance==null) c_instance=new SocratonsService();
    return c_instance;
  }
  
  private Socratons m_socratons=null;
  
  /**
   * <p>
   *   Gets
   *   {@link Socratons}.
   * </p>
   * @return {@link Socratons}.
   */
  public Socratons getSocratons() throws IOException
  {
    if (m_socratons==null)
    {
      InputStream is=null;
      try
      {
        is=request(m_getReq);
        
        String xml=IOUtil.read(is);
        System.err.println("xml from socraton service:");
        System.err.println(xml);
        
        m_socratons=parseCopy(xml);
      }
      finally
      {        
        if (is!=null) try { is.close(); } catch (IOException _){}
      }
    }
   
    return m_socratons;
  }
  
  /**
   * <p>
   *   Parse XML from service from a get request.
   * </p>
   * @param xml XML to parse.
   * @return Created.
   * @throws IOException If fails.
   */
  private Socratons parseCopy(String xml) throws IOException
  {
    if (xml==null)
    {
      throw new NullPointerException("xml");
    }
    
    ByteArrayInputStream is=new ByteArrayInputStream(xml.getBytes());
    return parseCopy(is);
  }
  
  /**
   * <p>
   *   Parse XML from service from a get request.
   * </p>
   * @param is Stream to parse.
   * @return Created.
   * @throws IOException If fails.
   */
  private Socratons parseCopy(InputStream is) throws IOException
  {
    SAXParserFactory f=SAXParserFactory.newInstance();
    f.setNamespaceAware(false);
    f.setValidating(false);
    try
    {
      SAXParser parser=f.newSAXParser();
      
      SocratonsXmlProtocolSaxHandler h=new SocratonsXmlProtocolSaxHandler();
      parser.parse(is, h);
      
      List<SocData> l=h.getParsed();
      
      return createFromParsed(l);
    }
    catch (ParserConfigurationException pcx)
    {
      IOException iox=new IOException();
      iox.initCause(pcx);
      throw iox;
    }
    catch (SAXException pcx)
    {
      IOException iox=new IOException();
      iox.initCause(pcx);
      throw iox;
    }
  }
  
  /**
   * <p>
   *   Creates
   *   {@link Socratons} from parsed XML from service.
   * </p>
   * @param l Parse result.
   * @return Created.
   */
  private static Socratons createFromParsed(List<SocData> l)
  {
    Socratons retVal=new Socratons();
    
    for (SocData sd : l)
    {
      Socraton s=sd.create();
      retVal.add(s, null);  // TODO NEED TO RESOLVE CREATION CTX.
    }
    
    return retVal;
  }
  
  /**
   * <p>
   *   Signals data store should be updated.
   * </p>
   * @throws IOException If fails.
   */
  public void update() throws IOException
  {
    if (m_socratons==null) return; // never fetched, nothing to do.
    
    InputStream is=null;
    
    try
    {
      SocratonsXmlProtocolCompiler comp=new SocratonsXmlProtocolCompiler();
      for (Socraton s : m_socratons)
      {
        if (s.changed())
        {
          comp.socraton(s);
        }
      }
    
      String xml=comp.compile();
      is=request(xml);
    }
    finally
    {
      if (is!=null) try { is.close(); } catch (IOException _){}
    }
  }
  
  // Code for sending XML request to server.
  
  /**
   * <p>
   *   Sends request XML to service.
   * </p>
   * @param xml XML defining request.
   * @return Stream to read response from.
   * @throws IOException If fails.
   */
  private InputStream request(String xml) throws IOException
  { 
    OutputStream os=null;
    
    try
    {
      URL url=new URL(m_url);    
      URLConnection con=url.openConnection();
      con.setDoOutput(true);

      os=con.getOutputStream();
      Writer w=new OutputStreamWriter(os);
      w.write(xml);
      w.flush();
  
      return con.getInputStream();
    }
    finally
    {
      if (os!=null) try { os.close(); } catch (IOException _){}
    }
  }
  
  public static void main(String[] args) 
  {
    try
    {
      SocratonsService ss=SocratonsService.getInstance();
      
      Socratons s=ss.getSocratons();
      
      System.out.println("---");
      System.out.println("socratons:");
      System.out.println();
      System.out.println(s);
    }
    catch (Exception x)
    {
      x.printStackTrace();
      System.exit(-1);
    }
  }
  
}
